#include "TestFunctions.h"
#include "Timer.h"
#include "Atomic.h"
#include "StackAllocator.h"
#include "DoubleStackAllocator.h"
#include "PoolAllocator.h"
#include "HeapAllocator.h"
#include "Assertions.h"
#include "FilePath.h"
#include "Console.h"
#include "Logging.h"
#include "File.h"
#include "UnitTest.h"
#include "GlobalVariables.h"
#include "MemoryManager.h"

#include <iostream>
#include <string>
#include <cstdio>

using namespace CEngine;

void
runUnitTests(void) {
    UnitTest tester;
    {
        tester.createTestGroup("FilePath");
        CHAR pathString[70] = "C:/Projects/ClockworkEngine/TestBed:\\\\..\\./TestBed\\testFile.txt";
        FilePath path(pathString, false);
        U32 length = 0;
        if(strcmp(path.getFullPath(&length),"C:/Projects/ClockworkEngine/TestBed:\\\\..\\./TestBed\\testFile.txt") == 0 && length == 63) {
            tester.test("FilePath", "GetFullPath", true);
        }
        else {
            tester.test("FilePath", "GetFullPath", false);
        }

        FilePath::processPath(pathString);
        path = pathString;
        if(strcmp(path.getFullPath(&length),"C:\\Projects\\ClockworkEngine\\TestBed\\..\\.\\TestBed\\testFile.txt") == 0 && length == 61) {
            tester.test("FilePath", "ProcessPath + operator=", true);
        }
        else {
            tester.test("FilePath", "ProcessPath + operator=", false);
        }

        I32 l = path.simplifyPath();
        if(strcmp(path.getFullPath(&length),"C:\\Projects\\ClockworkEngine\\TestBed\\testFile.txt") == 0 && length == 48 && l == -13) {
            tester.test("FilePath", "SimplifyPath", true);
        }
        else {
            tester.test("FilePath", "SimplifyPath", false);
        }

        U32 length2;
        path.getFileName(&length2, false);
        CHAR filenameArray[50];
        for (U32 i = 0; i < length2; ++i) {
            filenameArray[i] = path.getFileName()[i];
        }
        filenameArray[length2] = '\0';

        if(strcmp(path.getFileName(&length),"testFile.txt") == 0 && length == 12
           && strcmp(filenameArray,"testFile") == 0 && length2 == 8) {
            tester.test("FilePath", "GetFilename", true);
        }
        else {
            tester.test("FilePath", "GetFilename", false);
        }

        if(strcmp(path.getExtension(&length), ".txt") == 0 && length == 4) {
            tester.test("FilePath", "GetExtension", true);
        }
        else {
            tester.test("FilePath", "GetExtention", false);
        }

        if(!FilePath("TestFile.abx").pathExists() && path.pathExists()) {
            tester.test("FilePath", "pathExists", true);
        }
        else {
            tester.test("FilePath", "pathExists", false);
        }

        if(FilePath("TestFile.txt").isRelative() && !path.isRelative()) {
            tester.test("FilePath", "isRelative", true);
        }
        else {
            tester.test("FilePath", "isRelative", false);
        }
    }

    tester.printResults();
}

using namespace std;

void
gotoXY( int x, int y ) {
    COORD p = { x, y };
    SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), p );
}

void
drawHorizontalLine(U32 width) {
    for (U16 i = 0; i < width; ++i) {
        cout << (char)219;
    }
    cout << endl;
}

void
drawPageMap(U32 maxWidth, TRACKING_UNIT* pages, U32 length) {
    if (maxWidth % 2 == 0)
        --maxWidth;

    drawHorizontalLine(maxWidth);
    cout << (char)186;

    U32 index = 0;
    --index;
    TRACKING_UNIT mask = 1 << ((sizeof(TRACKING_UNIT) * 8) - 1);
    U32 linePosition = 1;

    for (U32 i = 0; i < length; ++i) {
        if (linePosition >= maxWidth) {
            cout << endl;
            drawHorizontalLine(maxWidth);
            cout << (char)186;
            linePosition = 1;
        }
        if (i % (sizeof(TRACKING_UNIT) * 8) == 0) {
            ++index;
            mask = 1 << ((sizeof(TRACKING_UNIT) * 8) - 1);
        }

        TRACKING_UNIT unit = *(pages + index);
        if ((unit & mask) == mask)
            cout << (char)219;
        else
            cout << ' ';

        cout << (char)186;
        linePosition += 2;
        mask = mask >> 1;
    }
    cout << endl;
    drawHorizontalLine(linePosition);
    cout << endl;
}

void
drawPointerList(int xOffset, int yOffset, vector<void*> pointers, CEngine::HeapAllocator* heap) {
    for (U32 i = 0; i < 23; ++i) {
        gotoXY(xOffset, yOffset + i);
        cout << "                           \r";
    }

    for (U32 i = 0; i < pointers.size(); ++i) {
        gotoXY(xOffset, yOffset + i);
        U32 pages = heap->debugReturnHeader(pointers[i]).size / heap->getPageSize();
        if (heap->debugReturnHeader(pointers[i]).size % heap->getPageSize() != 0)
            ++pages;
        cout << i << ". " << heap->debugReturnHeader(pointers[i]).size << " bytes. " << pages << " Pages.";
    }
}

U32
getDigits(U32 number) {
    U32 digits = 0;

    while(number > 0) {
        number /= 10;
        ++digits;
    }
    return digits;
}

void
radixSort(U32 elements[], const U32 arraySize, const U32 length, U32& count) {
    U32 store[arraySize];
    U32 binSizes[10];
    U32 binIndices[10];
    U32 m = 1;
    count = 0;

    for (U32 l = 0; l < length; ++l) {

        for (U8 i = 0; i < 10; ++i) {
            binSizes[i] = 0;
        }

        for (U32 i = 0; i < arraySize; ++i) {
            U32 temp = elements[i];
            temp /= m;
            ++binSizes[temp % 10];
            ++count;
        }

        for (U8 i = 0; i < 10; ++i) {
            binIndices[i] = 0;
        }

        for (U32 i = 0; i < arraySize; ++i) {
            U32 digit = elements[i];
            digit /= m;
            digit = digit % 10;
            ++count;

            U32 position = 0;
            for (U32 i = 0; i < digit; ++i) {
                position += binSizes[i];
            }
            position += binIndices[digit];
            ++binIndices[digit];

            store[position] = elements[i];
            ++count;
        }

        for (U32 i = 0; i < arraySize; ++i) {
            elements[i] = store[i];
            ++count;
        }

        m *= 10;
    }
}

void
top(const U32 size, fstream& file) {
    U32 width = 1 + ((size - 1) * 2);
    U32 height = 2 * size;
    U32 spaces = 6 + ((size - 1) * 4);

    for (U32 i = 0; i < spaces; ++i) {
        file << ' ';
    }
    for (U32 i = 0; i < width; ++i) {
        file << '#';
    }
    file << '\n';

    for (U32 i = 0; i < height - 2; ++i) {
        for (U32 i = 0; i < spaces; ++i) {
            file << ' ';
        }
        for (U32 i = 0; i < width; ++i) {
            file << '|';
        }
        file << '\n';
    }

    for (U32 i = 0; i < spaces; ++i) {
        file << ' ';
    }
    for (U32 i = 0; i < width; ++i) {
        file << '#';
    }

    file << std::endl;
}

void
middle(const U32 size, fstream& file) {
    U32 width = 5 + ((size - 1) * 2);
    U32 height = 1 + (3 + ((size - 2) * 2));
    U32 spaces = 4 + ((size - 1) * 4);

    for (U32 i = 0; i < spaces; ++i) {
        file << ' ';
    }
    for (U32 i = 0; i < width; ++i) {
        file << '~';
    }
    file << '\n';

    for (U32 i = 0; i < height; ++i) {
        for (U32 i = 0; i < spaces; ++i) {
            file << ' ';
        }

        file << '|' << '-';

        for (U32 i = 0; i < size; ++i) {
            file << 'O' << '-';
        }

        file << '|' << '\n';

        for (U32 i = 0; i < spaces; ++i) {
            file << ' ';
        }

        for (U32 i = 0; i < width; ++i) {
            file << '~';
        }
        file << std::endl;
    }
}

void
bottom(const U32 size, fstream& file) {
    U32 upperLines = 1 + size / 2;
    U32 lowerLines = size;
    U32 width = (10 * size) + 3;

    for (U32 i = 0; i < upperLines; ++i) {
        U32 spaces = ((upperLines - 1) * 3) - (i * 3);
        U32 length = (width - ((spaces * 2) + 3)) / 2;
        for (U32 i = 0; i < spaces; ++i) {
            file << ' ';
        }

        file << '/' << '"';

        for (U32 i = 0; i < length; ++i) {
            file << '\'' << '"';
        }
        file << '\\' << '\n';
    }

    for (U32 i = 0; i < lowerLines; ++i) {
        U32 length = (width - 3) / 2;
        file << '/' << '"';

        for (U32 i = 0; i < length; ++i) {
            file << 'O' << '"';
        }

        file << '\\' << std::endl;
    }
}

U64
hashString(const char* string) {
    U64 seed = 12764787846358441471;
    U64 hash = 0;
    for(int i = 0; string[i] != '\0'; i++) {
        hash = (hash * seed) + string[i];
    }
   return hash;
}

Test::Test(void)
{
    static U32 temp = 0;
    value1 = temp++;
}
